Skip to content

feat: state-space seasonal model matching R bsts AddSeasonal()#11

Merged
YuminosukeSato merged 9 commits into
mainfrom
feat/seasonal-state-model
Mar 23, 2026
Merged

feat: state-space seasonal model matching R bsts AddSeasonal()#11
YuminosukeSato merged 9 commits into
mainfrom
feat/seasonal-state-model

Conversation

@YuminosukeSato
Copy link
Copy Markdown
Owner

Summary

Replace the dummy-variable regression approach for seasonal components with a proper Kalman state-space model, matching R bsts AddSeasonal() behavior. This achieves <0.1% relative error vs R bsts for all seasonal metrics (down from ±5%).

  • Implement Durbin-Koopman (2002) simulation smoother for S-dimensional state vector [μ_t, s_1(t), ..., s_{S-1}(t)] with sum-to-zero seasonal transition
  • Add sample_sigma2_seasonal() with InvGamma conjugate posterior matching R bsts SdPrior
  • Add seasonal branch in Gibbs sampler with post-period seasonal state propagation
  • Tighten R numerical equivalence tolerances: seasonal ±5% → ±1%, basic/strong_effect CI ±1.5% → ±1.0%

Changes

File Change
src/kalman.rs +727 lines: local_level_seasonal_smoother(), helper functions, 17 unit tests
src/sampler.rs Seasonal Gibbs branch, sample_sigma2_seasonal(), post-period propagation, 10 unit tests
src/state_space.rs has_seasonal(), seasonal_nseasons(), seasonal_duration()
src/lib.rs sigma_seasonal field in PyO3 GibbsSamples
tests/test_seasonal_smoother.py 13 Python integration tests
tests/test_numerical_equivalence.py Remove xfail, tighten tolerances
tests/test_integration.py xfail for state-space seasonal regression test

Numerical equivalence (niter=20000)

Scenario Metric R value Python value Relative error
seasonal point_effect_mean 2.9822 2.9814 0.03%
seasonal ci_lower 2.7770 2.7781 0.04%
seasonal ci_upper 3.1868 3.1887 0.06%
basic ci_lower 2.3957 2.4063 0.44%
covariates ci_lower 2.4680 2.4581 0.40%

Test plan

  • 64 Rust unit tests pass (cargo test)
  • 236 Python tests pass, 1 xfailed (pytest tests/)
  • 33 R numerical equivalence tests pass with tightened tolerances
  • Non-seasonal code paths unchanged (verified by 200+ existing tests)
  • CI passes on GitHub Actions

Seasonal model is being migrated from static dummy-variable regressors
to Kalman state variables (R bsts AddSeasonal compat). Tests will break
during the transition and be re-enabled after implementation.
Implement Durbin-Koopman (2002) simulation smoother for S-dimensional
state vector [μ_t, s_1(t), ..., s_{S-1}(t)] with sum-to-zero seasonal
transition, matching R bsts AddSeasonal() behavior.

Includes helper functions (apply_state_transition, count_season_boundaries,
seasonal_kalman_smoother) and 17 unit tests covering boundary values
(T=1, S=2/7/12, season_duration=2/7), signal tracking, and numerical
stability.
- sampler.rs: Add seasonal branch in run_single_chain_static() with
  local_level_seasonal_smoother, sample_sigma2_seasonal (InvGamma
  conjugate posterior), sample_sigma2_obs_seasonal, and post-period
  seasonal state propagation. Add 10 unit tests for sigma2_seasonal.
  Remove dead code (scale_matrix, invert_matrix) from rust-speedup merge.
- state_space.rs: Store SeasonalConfig, add has_seasonal(),
  seasonal_nseasons(), seasonal_duration(). Skip dummy regressors when
  state-space seasonal is active.
- lib.rs: Add sigma_seasonal field to GibbsSamples PyO3 binding.
- Add tests/test_seasonal_smoother.py with 13 integration tests:
  sigma_seasonal existence/emptiness/positivity/length, point effect
  and CI bounds finiteness, post-period predictions, significance
  detection, backward compatibility, boundary values (S=2/12, d=7).
- xfail test_seasonal_model_tracks_weekly_pattern in test_integration.py:
  state-space seasonal adds propagation variance in post-period, making
  point estimates marginally less precise for constant seasonal patterns
  with low noise. This matches R bsts behavior.
State-space seasonal smoother achieves <0.1% relative error vs R bsts,
far exceeding the original ±5% tolerance with dummy-variable approach.

Tolerance changes:
- seasonal: ±5% → ±1% (actual: <0.1%)
- basic/strong_effect CI: ±1.5% → ±1.0% (actual: <0.5%)
- Remove xfail from TestEquivalenceSeasonal (all 5 tests pass)

All 33 numerical equivalence tests pass with tightened tolerances.
.cargo/config.toml set target-cpu=native for x86_64-unknown-linux-gnu,
causing SIGILL in CI when build scripts compiled with AVX instructions
ran on runners without AVX support. rust-cache amplified this by
sharing binaries across runners with different CPU feature sets.

Move .cargo/ to .gitignore so developers can opt into target-cpu=native
locally without affecting CI builds.
- Remove unused pytest import
- Remove unused ci variable assignment
- Fix line length (89 > 88)
- Fix import sorting
…d tightened tolerances

- Update CI tolerance from ±5% to ±1% for seasonal (README, compatibility-matrix)
- Add season_duration usage examples to docs/examples.md
- Document state-space seasonal model matching R bsts AddSeasonal()
- Add seasonal ±1% CI parity note to migration-from-r.md
- Fix season.duration param notation in compatibility-matrix.md
- Improve season_duration examples with clearer use cases
- Add nseasons=1 equivalence note and constraint rules to api.md
- Define Matching vs Supported status labels in README.md
@YuminosukeSato YuminosukeSato merged commit d47c683 into main Mar 23, 2026
14 checks passed
YuminosukeSato added a commit that referenced this pull request Mar 23, 2026
feat: state-space seasonal model matching R bsts AddSeasonal()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant